#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "src/list.h"


// printf("%-64s", "\x1b[33m" # func "\x1b[0m");
#define test(func) \
    printf("... %-64s", "\033[1;33m" #func "\033[0m");      \
    test_##func();                              \
    printf("\033[1;32m ok \033[0m\n")



void elem_free(void *val)
{(void)val;}

int elem_cmp(const void *a, const void *b)
{
    return strncmp(a, b, strlen(a));
}

void *elem_copy(void *src, void *match)
{
    if (match) {
        if (strncmp(match, src, strlen(match)))
            return NULL;
    }
    return strdup(src);
}


void test_list_new()
{
    list_t *l = NULL;
    l = list_new(128, NULL);
    assert(l != NULL);
    assert(l->size == 128);
    assert(l->cb_free == NULL);
    assert(l->elems != NULL);

    list_free(l);

    l = list_new(1024, elem_free);
    assert(l != NULL);
    assert(l->size == 1024);
    assert(l->cb_free == elem_free);
    assert(l->elems != NULL);

    list_free(l);
}


void test_list_add()
{

}

void test_list_add_noduplicate()
{
    list_t *l;
    l = list_new(128, elem_free);
    assert(l != NULL);

    char *a = "foo";
    char *b = "tar";
    char *c = "hoo";
    char *d = "pop";

    list_add_noduplicate(l, a, elem_cmp);
    list_add_noduplicate(l, b, elem_cmp);
    list_add_noduplicate(l, c, elem_cmp);
    assert(l->cnt == 3);

    list_add_noduplicate(l, a, elem_cmp);
    list_add_noduplicate(l, b, elem_cmp);
    assert(l->cnt == 3);

    list_add_noduplicate(l, d, elem_cmp);
    assert(l->cnt == 4);

    list_free(l);
}


void test_list_grow()
{
    list_t *l;
    l = list_new(2, elem_free);
    assert(l != NULL);
    assert(l->size == 2);

    char *a = "foo";
    char *b = "tar";
    char *c = "hoo";
    char *d = "pop";

    list_add(l, a);
    list_add(l, b);
    list_add(l, c);
    list_add(l, d);
    assert(l->size == 4);
    assert(l->cnt  == 4);

    list_add(l, a);
    assert(l->size == 8);
    assert(l->cnt  == 5);

    list_free(l);
}


void test_list_find()
{
    list_t *l;
    l = list_new(32, elem_free);
    assert(l != NULL);
    assert(l->size == 32);

    char *a = "foo";
    char *b = "tar";
    char *c = "hoo";
    char *d = "pop";

    list_add(l, a);
    list_add(l, b);
    list_add(l, c);

    assert (a == list_find(l, a, elem_cmp));
    assert (b == list_find(l, b, elem_cmp));
    assert (c == list_find(l, c, elem_cmp));

    list_add(l, d);
    assert (d == list_find(l, d, elem_cmp));

    list_free(l);
}


void test_list_remove()
{
    list_t *l;
    l = list_new(32, elem_free);
    assert(l != NULL);
    assert(l->size == 32);

    char *a = "foo";
    char *b = "tar";
    char *c = "hoo";
    char *d = "pop";

    list_add(l, a);
    list_add(l, b);
    list_add(l, c);
    assert (b == list_find(l, b, elem_cmp));

    list_remove(l, b);
    assert (b != list_find(l, b, elem_cmp));

    list_add(l, b);
    list_add(l, d);

    list_remove(l, d);
    assert (d != list_find(l, b, elem_cmp));

    list_free(l);
}


void test_list_remove_cmp()
{
    list_t *l;
    l = list_new(32, elem_free);
    assert(l != NULL);
    assert(l->size == 32);

    char *a = "foo";
    char *b = "tar";
    char *c = "hoo";
    char *d = "pop";

    list_add(l, a);
    list_add(l, b);
    list_add(l, c);
    assert (b == list_find(l, b, elem_cmp));

    list_remove_cmp(l, b, elem_cmp);
    assert (b != list_find(l, b, elem_cmp));

    list_add(l, b);
    list_add(l, d);

    list_remove_cmp(l, d, elem_cmp);
    assert (d != list_find(l, b, elem_cmp));

    list_free(l);
}


void test_list_multi_remove_cmp()
{
    list_t *l;
    l = list_new(32, elem_free);
    assert(l != NULL);
    assert(l->size == 32);

    char *a = "foo";
    char *b = "tar";

    list_add(l, a);
    list_add(l, a);
    list_add(l, a);
    list_add(l, b);
    list_add(l, b);
    assert(l->cnt == 5);

    assert(list_find(l, a, elem_cmp) != NULL);
    assert(list_find(l, b, elem_cmp) != NULL);

    int cnt;
    cnt = list_multi_remove_cmp(l, a, elem_cmp);
    assert(cnt == 3);
    assert(list_find(l, a, elem_cmp) == NULL);
    assert(l->cnt == 2);

    cnt = list_multi_remove_cmp(l, b, elem_cmp);
    assert(cnt == 2);
    assert(list_find(l, a, elem_cmp) == NULL);
    assert(l->cnt == 0);

    list_free(l);
}


void test_list_sort()
{
    list_t *l;
    l = list_new(32, elem_free);
    assert(l != NULL);
    assert(l->size == 32);

    char *a = "aaa";
    char *b = "bbb";
    char *c = "ccc";
    char *d = "ddd";
    char *e = "eee";

    list_add(l, c);
    list_add(l, a);
    list_add(l, e);
    list_add(l, d);
    list_add(l, b);

    assert(list_elem(l, 0) == c);
    assert(list_elem(l, 1) == a);
    assert(list_elem(l, 2) == e);
    assert(list_elem(l, 3) == d);
    assert(list_elem(l, 4) == b);
    assert((l->flags & LIST_F_SORTED) == 0);

    list_sort(l, elem_cmp);

    assert(list_elem(l, 0) == a);
    assert(list_elem(l, 1) == b);
    assert(list_elem(l, 2) == c);
    assert(list_elem(l, 3) == d);
    assert(list_elem(l, 4) == e);
    assert((l->flags & LIST_F_SORTED) != 0);

    list_free(l);
}


void test_list_copy()
{
    list_t *l;
    l = list_new(32, elem_free);
    assert(l != NULL);
    assert(l->size == 32);

    char *a = "aaa";
    char *b = "bbb";
    char *c = "ccc";
    char *d = "ddd";
    char *e = "eee";

    list_add(l, c);
    list_add(l, a);
    list_add(l, e);
    list_add(l, d);
    list_add(l, b);

    list_t *l2 = list_copy(l, elem_copy, NULL);
    assert(l2->size == l->size);
    assert(l2->cnt == l->cnt);
    assert(l2->flags == l->flags);
    assert(l2->cb_free == l2->cb_free);
    assert(strcmp(list_elem(l2, 0), list_elem(l, 0)) == 0);
    assert(strcmp(list_elem(l2, 1), list_elem(l, 1)) == 0);
    assert(strcmp(list_elem(l2, 2), list_elem(l, 2)) == 0);
    assert(strcmp(list_elem(l2, 3), list_elem(l, 3)) == 0);
    assert(strcmp(list_elem(l2, 4), list_elem(l, 4)) == 0);

    list_add(l, a);
    list_add(l, a);   // 包括3个a
    list_t *l3 = list_copy(l, elem_copy, a);
    assert(l3->cnt == 3);
    assert(l3->size == l->size);
    assert(l3->cb_free == l2->cb_free);
    assert(list_multi_remove_cmp(l3, a, elem_cmp) == 3);
    assert(l3->cnt == 0);

    list_free(l);
    list_free(l2);
    list_free(l3);
    /* not free strdup */
}


int main(int argc, char *argv[])
{
    test(list_new);
    test(list_add);
    test(list_add_noduplicate);
    test(list_grow);
    test(list_find);
    test(list_remove);
    test(list_remove_cmp);
    test(list_multi_remove_cmp);
    test(list_sort);
    test(list_copy);

    puts("... \x1b[32m100%\x1b[0m\n");
    return 0;
}

